package com.ruoyi.platform.system.role.service;

import com.github.pagehelper.PageHelper;
import com.ruoyi.base.enums.ExceptionEnum;
import com.ruoyi.base.exception.SysException;
import com.ruoyi.base.page.PageData;
import com.ruoyi.base.redis.role.RoleMenuRedis;
import com.ruoyi.base.redis.role.UserMenuRedis;
import com.ruoyi.base.utils.thread.ThreadUtils;
import com.ruoyi.platform.system.menu.enums.MenuEnum;
import com.ruoyi.platform.system.menu.pojo.MenuTreeDTO;
import com.ruoyi.platform.system.role.dao.RoleRdbDAO;
import com.ruoyi.platform.system.role.dao.menu.RoleMenuRdbDAO;
import com.ruoyi.platform.system.role.manager.RoleManager;
import com.ruoyi.platform.system.role.pojo.RoleDO;
import com.ruoyi.platform.system.role.pojo.menu.RoleMenuDO;
import com.ruoyi.platform.system.user.dao.role.UserRoleDAO;
import com.ruoyi.platform.system.user.manager.UserManager;
import com.ruoyi.platform.system.user.pojo.role.UserRoleDO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 角色信息表
 *
 * @author lcf
 * @version 0.0.1
 * @since 2020-11-16 01:47:41
 */
@Service
@Scope("prototype")
public class RoleService {
    @Autowired
    private RoleRdbDAO roleDao;
    @Autowired
    private UserRoleDAO userRoleRdbDAO;
    @Autowired
    private RoleMenuRdbDAO roleMenuRdbDAO;
    @Autowired
    private RoleMenuRedis roleMenuRedis;
    @Autowired
    private UserMenuRedis userMenuRedis;
    @Autowired
    private RoleManager roleManager;
    @Autowired
    private UserManager userManager;

    /**
     * 新增角色
     */
    public void addRole(String roleName) {
        checkRoleNameExist(null, roleName);

        roleDao.insertRoleSelective(new RoleDO(roleName));
    }

    /**
     * 修改角色
     */
    public void editRole(long roleId, String roleName) {
        roleManager.checkSysRole(roleId);
        checkRoleNameExist(roleId, roleName);

        roleDao.updateRoleByPrimaryKeySelective(new RoleDO(roleId, roleName));
    }

    /**
     * 删除角色
     */
    @Transactional(rollbackFor = Exception.class)
    public void delRole(long roleId) {
        roleManager.checkSysRole(roleId);

        roleDao.delRoleByPrimaryKey(roleId);
        roleMenuRdbDAO.delByRoleId(roleId);
        roleMenuRedis.delCache(roleId);

        List<UserRoleDO> userRoleList = userRoleRdbDAO.selectByRoleId(roleId);
        if (CollectionUtils.isNotEmpty(userRoleList)) {
            ThreadUtils.addTask(() -> {
                List<Long> userIdList = userRoleList.stream().map(UserRoleDO::getUserId).collect(Collectors.toList());
                userIdList.forEach(x -> userMenuRedis.delCache(x));
            });
        }

        userRoleRdbDAO.delByRoleId(roleId);
    }

    /**
     * 批量删除角色
     */
    @Transactional(rollbackFor = Exception.class)
    public void delRoleList(List<Long> roleIdList) {
        if (CollectionUtils.isEmpty(roleIdList)) {
            return;
        }

        roleIdList.forEach(this::delRole);
    }

    /**
     * 获取角色列表
     */
    public PageData<RoleDO, RoleDTO> getRoleList(String roleName, int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<RoleDO> roleList = roleDao.selectByRoleName(roleName);
        if (CollectionUtils.isEmpty(roleList)) {
            return new PageData<>(roleList);
        }

        return new PageData<>(roleList, RoleDTO::new);
    }

    /**
     * 设置角色菜单权限
     */
    public void editRoleMenu(long roleId, List<String> menuCodeList) {
        roleManager.checkSuperRole(roleId);

        roleMenuRdbDAO.delByRoleId(roleId);

        roleMenuRedis.delCache(roleId);

        //刷新已登陆用户权限
        if (CollectionUtils.isEmpty(menuCodeList)) {
            roleManager.refreshCurrentUserPermissionByRoleId(roleId);
            return;
        }

        List<MenuEnum> menuList = menuCodeList.stream().map(MenuEnum::ofCode).filter(Objects::nonNull).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(menuList)) {
            return;
        }

        List<MenuEnum> parentMenuList = new ArrayList<>();
        menuList.forEach(x -> addParentMenuList(parentMenuList, menuList, x));
        if (CollectionUtils.isNotEmpty(parentMenuList)) {
            menuList.addAll(parentMenuList);
        }

        List<RoleMenuDO> roleMenuList = menuList.stream().map(x -> new RoleMenuDO(roleId, x)).collect(Collectors.toList());
        roleMenuRdbDAO.insertRoleMenuSelectiveBatch(roleMenuList);

        //刷新已登陆用户权限
        roleManager.refreshCurrentUserPermissionByRoleId(roleId);
    }

    /**
     * 添加父菜单
     */
    private void addParentMenuList(List<MenuEnum> parentMenuList, List<MenuEnum> menuList, MenuEnum menuEnum) {
        if (menuEnum == null) {
            return;
        }

        if (Objects.equals(menuEnum.getParentCode(), "0")) {
            return;
        }

        MenuEnum existParent = getParentByList(menuEnum.getParentCode(), menuList);
        if (existParent != null) {
            addParentMenuList(parentMenuList, menuList, existParent);
            return;
        }

        MenuEnum parent = MenuEnum.ofCode(menuEnum.getParentCode());
        if (parent == null) {
            return;
        }

        if (parentMenuList.stream().noneMatch(x -> Objects.equals(x.getCode(), parent.getCode()))) {
            parentMenuList.add(parent);
        }

        addParentMenuList(parentMenuList, menuList, parent);
    }

    /**
     * 从指定菜单列表找出父菜单
     */
    public MenuEnum getParentByList(String parentCode, List<MenuEnum> list) {
        return list.stream().filter(m -> Objects.equals(parentCode, m.getCode())).findAny().orElse(null);
    }

    /**
     * 获取角色菜单列表
     */
    public List<MenuTreeDTO> getRoleMenuTree(long roleId) {
        roleManager.checkRoleId(roleId);
        return roleMenuRedis.getList(roleId);
    }

    /**
     * 检查角色是否存在
     */
    private void checkRoleNameExist(Long roleId, String roleName) {
        if (StringUtils.isEmpty(roleName)) {
            return;
        }

        List<RoleDO> roleList = roleDao.selectByRole(new RoleDO(roleName));
        if (CollectionUtils.isEmpty(roleList)) {
            return;
        }

        if (roleList.stream().anyMatch(x -> !Objects.equals(roleId, x.getRoleId()))) {
            throw new SysException(ExceptionEnum.ROLE_NAME_EXIST);
        }
    }
}